import { createContext, useContext, useEffect, useState } from "react";

// 判断是否有变化的数据
function changed (oldState, newState) {
    let changed = false;
    for (const key in oldState) {
        if (oldState[key] !== newState[key]) {
            changed = true;
        } 
    }
    return changed;
}

let state = undefined;
let reducer = undefined;
let listeners = [];
const setState = (newState) => {
    state = newState;
    listeners.map(fn => fn(state));
}

const store = {
    getState () {
        return state;
    },
    dispatch: (action) => {
        setState(reducer(state, action));
    },

    subscribe (fn) {
        listeners.push(fn);
        return () => {
            const index = listeners.indexOf(fn);
            listeners.splice(index, 1); // 取消订阅
        }
    },
}

let dispatch = store.dispatch;
const prevDispatch1 = dispatch;
dispatch = action => {
    if (action instanceof Function) {
        action(dispatch);
    } else {
        prevDispatch1(action);
    }
}

const prevDispatch2 = dispatch;
dispatch = action => {
    if (action.payload instanceof Promise) {
        action.payload.then(data => {
            dispatch({...action, payload: data});
        })
    } else {
        prevDispatch2(action);
    }
}

// 上下文
export const appContext = createContext(null);

// 生成 store
export const createStore = (_reducer, initState) => {
    state = initState;
    reducer = _reducer;
    return store;
}

// 高阶组件
export const connect = (selector, dispatchSelector) => (Component) => {
    const Wrapper = (props) => {
        // dispatch 用于规范 setState 的流程
        // const dispatch = (action) => {
        //     setState(reducer(state, action));
        // }
        // const { setState } = store; // useContext(appContext); // 也可以通过 useContext(appContext) 获取  state, setState
        const [, update] = useState({}); // 只会一个组件的更新,有 bug, 解决方案,使用发布订阅
        
        const data = selector ? selector(state) : {state};
        const dispatchs =  dispatchSelector ? dispatchSelector(dispatch) : {dispatch};

        useEffect(() => {
            // 订阅, 解决只刷新一个组件的问题
            const unsubscrib = store.subscribe(() => {
                const newData = selector ? selector(state) : {state};
                // 判断数据是否更新,减少不必要的更新
                if (changed(data, newData)) {
                    console.log('update');
                    update({});
                }
            });
            return unsubscrib; // 解决可能多次订阅
        }, [selector])

        return <Component {...props} {...data} {...dispatchs}/>
    }

    return Wrapper;
}

// Provider 数据注入
export const Provider = ({store, children}) => {
    return (
        <appContext.Provider value={store}>
            {children}
        </appContext.Provider>
    )
}